/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang;
import jdk.internal.misc.TerminatingThreadLocal;

import java.lang.ref.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/**
 * 此类提供线程本地变量。这些变量与其普通对应变量的不同之处在于，
 * 每个访问变量的线程（通过其 {@code get} 或 {@code set} 方法）
 * 都有自己独立初始化的变量副本。{@code ThreadLocal} 实例通常是
 * 希望将状态与线程关联的类中的私有静态字段（例如，用户 ID 或事务 ID）。
 *
 * <p>例如，下面的类生成每个线程本地的唯一标识符。
 * 线程的 ID 在第一次调用 {@code ThreadId.get()} 时分配，
 * 在后续调用中保持不变。
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // 包含要分配的下一个线程 ID 的原子整数
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // 包含每个线程 ID 的线程本地变量
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // 返回当前线程的唯一 ID，必要时分配它
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>只要线程存活且 {@code ThreadLocal} 实例可访问，
 * 每个线程都持有对其线程本地变量副本的隐式引用；
 * 线程消失后，其所有线程本地实例副本都会被垃圾收集
 * （除非存在对这些副本的其他引用）。
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */
public class ThreadLocal<T> {
    /**
     * ThreadLocals 依赖于附加到每个线程的每线程线性探测哈希映射
     * （Thread.threadLocals 和 inheritableThreadLocals）。
     * ThreadLocal 对象充当键，通过 threadLocalHashCode 搜索。
     * 这是一个自定义哈希码（仅在 ThreadLocalMaps 内有用），
     * 它消除了连续构造的 ThreadLocals 被相同线程使用的常见情况下的冲突，
     * 同时在不太常见的情况下保持良好的行为。
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * 要分发的下一个哈希码。原子更新。从零开始。
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * 连续生成的哈希码之间的差异 - 将隐式顺序线程本地 ID 转换为
     * 2 的幂大小表的近乎最优分布的乘法哈希值。
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * 返回下一个哈希码。
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

    /**
     * 返回此线程本地变量的当前线程的"初始值"。
     * 此方法将在线程第一次使用 {@link #get} 方法访问变量时被调用，
     * 除非线程之前调用了 {@link #set} 方法，
     * 在这种情况下，不会为该线程调用 {@code initialValue} 方法。
     * 通常，此方法每个线程最多调用一次，
     * 但在后续调用 {@link #remove} 后跟 {@link #get} 的情况下可能会再次调用。
     *
     * <p>此实现简单地返回 {@code null}；
     * 如果程序员希望线程本地变量具有除 {@code null} 之外的初始值，
     * 则必须子类化 {@code ThreadLocal} 并重写此方法。
     * 通常，将使用匿名内部类。
     *
     * @return 此线程本地变量的初始值
     */
    protected T initialValue() {
        return null;
    }

    /**
     * 创建一个线程本地变量。变量的初始值通过在 {@code Supplier} 上调用 {@code get} 方法确定。
     *
     * @param <S> 线程本地变量值的类型
     * @param supplier 用于确定初始值的供应商
     * @return 新的线程本地变量
     * @throws NullPointerException 如果指定的供应商为 null
     * @since 1.8
     */
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

    /**
     * 创建一个线程本地变量。
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
    }

    /**
     * 返回此线程本地变量的当前线程副本中的值。
     * 如果变量对于当前线程没有值，
     * 则首先将其初始化为调用 {@link #initialValue} 方法返回的值。
     *
     * @return 此线程本地变量的当前线程值
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * 如果此线程本地变量的当前线程副本中有值，则返回 {@code true}，
     * 即使该值为 {@code null}。
     *
     * @return 如果当前线程在此线程本地变量中有关联值，则返回 {@code true}；否则返回 {@code false}
     */
    boolean isPresent() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        return map != null && map.getEntry(this) != null;
    }

    /**
     * set() 的变体，用于建立 initialValue。在用户重写了 set() 方法的情况下使用，
     * 而不是使用 set()。
     *
     * @return 初始值
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }

    /**
     * 将此线程本地变量的当前线程副本设置为指定值。
     * 大多数子类不需要重写此方法，
     * 仅依赖 {@link #initialValue} 方法来设置线程本地变量的值。
     *
     * @param value 要存储在此线程本地变量的当前线程副本中的值
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

    /**
     * 移除此线程本地变量的当前线程值。
     * 如果此线程本地变量随后被当前线程{@linkplain #get 读取}，
     * 其值将通过调用其 {@link #initialValue} 方法重新初始化，
     * 除非其值在此期间被当前线程{@linkplain #set 设置}。
     * 这可能导致在当前线程中多次调用 {@code initialValue} 方法。
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

    /**
     * 获取与 ThreadLocal 关联的映射。在 InheritableThreadLocal 中被重写。
     *
     * @param  t 当前线程
     * @return 映射
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * 创建与 ThreadLocal 关联的映射。在 InheritableThreadLocal 中被重写。
     *
     * @param t 当前线程
     * @param firstValue 映射初始条目的值
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    /**
     * 创建继承线程本地变量映射的工厂方法。
     * 设计为仅从 Thread 构造方法调用。
     *
     * @param  parentMap 与父线程关联的映射
     * @return 包含父线程可继承绑定的映射
     */
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

    /**
     * 方法 childValue 在子类 InheritableThreadLocal 中可见地定义，
     * 但在此处内部定义，以便提供 createInheritedMap 工厂方法，
     * 而无需在 InheritableThreadLocal 中子类化映射类。
     * 这种技术优于在方法中嵌入 instanceof 测试的替代方案。
     */
    T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

    /**
     * ThreadLocal 的扩展，从指定的 {@code Supplier} 获取其初始值。
     */
    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }

    /**
     * ThreadLocalMap 是一个定制的哈希映射，仅适用于维护线程本地值。
     * 没有操作导出到 ThreadLocal 类之外。该类是包私有的，
     * 以允许在 Thread 类中声明字段。为了帮助处理非常大和长期存在的使用情况，
     * 哈希表条目使用 WeakReferences 作为键。但是，由于不使用引用队列，
     * 只有当表开始空间不足时，才保证删除过时条目。
     */
    static class ThreadLocalMap {

        /**
         * 此哈希映射中的条目扩展 WeakReference，使用其主要 ref 字段作为键
         * （始终是 ThreadLocal 对象）。请注意，null 键（即 entry.get() == null）
         * 意味着键不再被引用，因此可以从表中删除该条目。
         * 在后续代码中，此类条目被称为"过时条目"。
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** 与此 ThreadLocal 关联的值。 */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * 初始容量 -- 必须是 2 的幂。
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * 表，根据需要调整大小。
         * table.length 必须始终是 2 的幂。
         */
        private Entry[] table;

        /**
         * 表中条目的数量。
         */
        private int size = 0;

        /**
         * 下一个要调整大小的大小值。
         */
        private int threshold; // Default to 0

        /**
         * 设置调整大小阈值以维持最坏情况下 2/3 的负载因子。
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /**
         * 增加 i 模 len。
         */
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /**
         * 减少 i 模 len。
         */
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

        /**
         * 构造一个最初包含 (firstKey, firstValue) 的新映射。
         * ThreadLocalMaps 是延迟构造的，所以我们只在至少有一个条目要放入时才创建一个。
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        /**
         * 构造一个新映射，包括来自给定父映射的所有可继承 ThreadLocals。
         * 仅由 createInheritedMap 调用。
         *
         * @param parentMap 与父线程关联的映射。
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        /**
         * 获取与键关联的条目。此方法本身仅处理快速路径：
         * 现有键的直接命中。否则它会中继到 getEntryAfterMiss。
         * 这样设计是为了最大化直接命中的性能，部分通过使此方法易于内联。
         *
         * @param  key 线程本地对象
         * @return 与键关联的条目，如果没有则返回 null
         */
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

        /**
         * getEntry 方法的版本，用于在直接哈希槽中找不到键时使用。
         *
         * @param  key 线程本地对象
         * @param  i 键的哈希码的表索引
         * @param  e table[i] 处的条目
         * @return 与键关联的条目，如果没有则返回 null
         */
        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

        /**
         * 设置与键关联的值。
         *
         * @param key 线程本地对象
         * @param value 要设置的值
         */
        private void set(ThreadLocal<?> key, Object value) {

            // 我们不像 get() 那样使用快速路径，因为使用 set() 创建新条目
            // 至少与替换现有条目一样常见，在这种情况下，
            // 快速路径失败的次数会更多。

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        /**
         * 移除键的条目。
         */
        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

        /**
         * 用指定键的条目替换在设置操作期间遇到的过时条目。
         * 无论指定键是否已存在条目，value 参数中传递的值都存储在条目中。
         *
         * 作为副作用，此方法会删除包含过时条目的"运行"中的所有过时条目。
         * （运行是两个 null 槽之间的条目序列。）
         *
         * @param  key 键
         * @param  value 要与键关联的值
         * @param  staleSlot 搜索键时遇到的第一个过时条目的索引。
         */
        private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;

            // 向后检查当前运行中的先前过时条目。
            // 我们一次清理整个运行，以避免由于垃圾收集器
            // 成批释放引用（即每当收集器运行时）而导致的持续增量重新哈希。
            int slotToExpunge = staleSlot;
            for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            // 找到键或运行的尾随 null 槽，以先出现的为准
            for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();

                // 如果我们找到键，那么我们需要将其与过时条目交换
                // 以维持哈希表顺序。
                // 新的过时槽或在其上方遇到的任何其他过时槽
                // 然后可以发送到 expungeStaleEntry
                // 以删除或重新哈希运行中的所有其他条目。
                if (k == key) {
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    // 如果存在先前的过时条目，则从该条目开始删除
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                // 如果我们在向后扫描时没有找到过时条目，
                // 那么在扫描键时看到的第一个过时条目
                // 是运行中仍然存在的第一个。
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            // 如果未找到键，则在过时槽中放入新条目
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);

            // 如果运行中还有任何其他过时条目，则删除它们
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }

        /**
         * 通过重新哈希位于 staleSlot 和下一个 null 槽之间的任何可能冲突的条目来删除过时条目。
         * 这也会删除在尾随 null 之前遇到的任何其他过时条目。参见 Knuth，第 6.4 节
         *
         * @param staleSlot 已知具有 null 键的槽的索引
         * @return staleSlot 之后下一个 null 槽的索引
         * （staleSlot 和此槽之间的所有条目都将被检查是否删除）。
         */
        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // 删除 staleSlot 处的条目
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // 重新哈希直到遇到 null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // 与 Knuth 6.4 算法 R 不同，我们必须扫描到 null，
                    // 因为可能有多个条目已过时。
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

        /**
         * 启发式地扫描一些单元格以查找过时条目。
         * 当添加新元素或删除另一个过时条目时调用此方法。
         * 它执行对数数量的扫描，作为不扫描（快速但保留垃圾）和
         * 与元素数量成比例的扫描数量之间的平衡，后者会找到所有垃圾
         * 但会导致某些插入花费 O(n) 时间。
         *
         * @param i 已知不包含过时条目的位置。扫描从 i 之后的元素开始。
         *
         * @param n 扫描控制：扫描 {@code log2(n)} 个单元格，
         * 除非找到过时条目，在这种情况下会扫描 {@code log2(table.length)-1} 个额外单元格。
         * 从插入调用时，此参数是元素数量，但从 replaceStaleEntry 调用时，它是表长度。
         * （注意：通过加权 n 而不是仅使用直接 log n，可以将所有这些更改为更激进或更不激进。
         * 但这个版本简单、快速，并且似乎工作得很好。）
         *
         * @return 如果删除了任何过时条目，则返回 true。
         */
        private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
                i = nextIndex(i, len);
                Entry e = tab[i];
                if (e != null && e.get() == null) {
                    n = len;
                    removed = true;
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);
            return removed;
        }

        /**
         * 重新打包和/或调整表的大小。首先扫描整个表以删除过时条目。
         * 如果这不能充分缩小表的大小，则将表大小加倍。
         */
        private void rehash() {
            expungeStaleEntries();

            // 使用较低的阈值进行加倍以避免滞后
            if (size >= threshold - threshold / 4)
                resize();
        }

        /**
         * 将表的容量加倍。
         */
        private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];
                if (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null; // 帮助 GC
                    } else {
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }

        /**
         * 删除表中的所有过时条目。
         */
        private void expungeStaleEntries() {
            Entry[] tab = table;
            int len = tab.length;
            for (int j = 0; j < len; j++) {
                Entry e = tab[j];
                if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}
